#include "dspSimulator/instruction/MVK.h"

namespace dspsimulator {
    std::shared_ptr<DspInstruction>
    MVK::decode_standard_instruction(const std::shared_ptr<DspInstructionInfo> &dsp_instruction_info) {
        std::shared_ptr<MVK> mvk;
        std::bitset<32> &data = dsp_instruction_info->m_standard_bits;
        if (common::check_bits<5>(data, 0b01010, 2) &&
            exist_standard_condition(common::extract_bits<4>(data, 28))) {
            // MVK .S scst16, sint
            mvk = std::make_shared<MVK>(dsp_instruction_info->address(), 4);
            mvk->m_creg_z = common::extract_bits<4>(data, 28);
            u32 dst = common::extract_bits<5>(data, 23).to_ulong();
            u32 cst = common::extract_bits<16>(data, 7).to_ulong();
            bool s = data[1];
            mvk->m_p = data[0];
            mvk->m_func_unit = s ? FuncUnit::S1 : FuncUnit::S2;
            mvk->m_op1 = std::make_shared<Immediate32>(common::extract_bit_to_int<16>(cst));
            mvk->m_op2 = std::make_shared<SingleReg>(get_dst_reg32_kind(dst, s));
        } else if (common::check_bits<10>(data, 0b0011010110, 2) && common::check_bits<5>(data, 0b00101, 13) &&
                   exist_standard_condition(common::extract_bits<4>(data, 28))) {
            // MVK .L scst5, sint
            mvk = std::make_shared<MVK>(dsp_instruction_info->address(), 4);
            mvk->m_creg_z = common::extract_bits<4>(data, 28);
            u32 dst = common::extract_bits<5>(data, 23).to_ulong();
            u32 cst = common::extract_bits<5>(data, 18).to_ulong();
            bool x = data[12];
            bool s = data[1];
            mvk->m_p = data[0];
            mvk->m_func_unit = s ? FuncUnit::L1 : FuncUnit::L2;
            mvk->m_op1 = std::make_shared<Immediate32>(common::extract_bit_to_int<5>(cst));
            mvk->m_op2 = std::make_shared<SingleReg>(get_dst_reg32_kind(dst, s));
        } else if (exist_standard_condition(common::extract_bits<4>(data, 28)) &&
                   common::check_bits<11>(data, 0b00000010000, 2)) {
            // MVK .D scst5,sint
            mvk = std::make_shared<MVK>(dsp_instruction_info->address(), 4);
            mvk->m_creg_z = common::extract_bits<4>(data, 28);
            u32 dst = common::extract_bits<5>(data, 23).to_ulong();
            u32 src2 = common::extract_bits<5>(data, 18).to_ulong();
            u32 cst5 = common::extract_bits<5>(data, 13).to_ulong();
            bool s = data[1];
            mvk->m_p = data[0];
            mvk->m_func_unit = s ? FuncUnit::D1 : FuncUnit::D2;
            mvk->m_op1 = std::make_shared<Immediate32>(common::extract_bit_to_int<5>(cst5));
            mvk->m_op2 = std::make_shared<SingleReg>(get_dst_reg32_kind(dst, s));
        }
        return mvk;
    }

    std::shared_ptr<DspInstruction>
    MVK::decode_compact_instruction(const std::shared_ptr<DspInstructionInfo> &dsp_instruction_info) {
        std::shared_ptr<MVK> mvk;
        std::bitset<16> &data = dsp_instruction_info->m_compact_bits;
        if (common::check_bits<6>(data, 0b010011, 1) && data[10]) {
            mvk = std::make_shared<MVK>(dsp_instruction_info->address(), 2);
            u32 dst = common::extract_bits<3>(data, 7).to_ulong();
            u32 scst = common::extract_bits<2>(data, 11).to_ulong() << 3;
            scst += common::extract_bits<3>(data, 13).to_ulong();
            mvk->m_p = dsp_instruction_info->p();
            bool s = data[0];
            mvk->m_func_unit = s ? FuncUnit::L1 : FuncUnit::L2;
            mvk->m_op1 = std::make_shared<Immediate32>(common::extract_bit_to_int<5>(scst));
            mvk->m_op2 = std::make_shared<SingleReg>(get_dst_reg32_kind(dst, s));
        } else if (common::check_bits<4>(data, 0b1001, 1)) {
            mvk = std::make_shared<MVK>(dsp_instruction_info->address(), 2);
            bool s = data[0];
            u32 ucst8 = common::extract_bits<3>(data, 13).to_ulong();
            ucst8 += common::extract_bits<2>(data, 11).to_ulong() << 3;
            ucst8 += common::extract_bits<2>(data, 5).to_ulong() << 5;
            ucst8 += common::extract_bits<1>(data, 10).to_ulong() << 7;
            u32 dst = common::extract_bits<3>(data, 7).to_ulong();
            mvk->m_func_unit = s ? FuncUnit::L1 : FuncUnit::L2;
            mvk->m_op1 = std::make_shared<Immediate32>(ucst8);
            mvk->m_op2 = std::make_shared<SingleReg>(get_dst_reg32_kind(dst, s));
            mvk->m_p = dsp_instruction_info->p();
        }
        return mvk;
    }

    std::string MVK::to_string() const {
        return get_standard_condition(m_creg_z) + m_name + " " + get_function_unit_name(m_func_unit) + " " +
               m_op1->to_string() + "," + m_op2->to_string();
    }

    void MVK::generate_ir(const std::shared_ptr<simulator::IRGenElement> &ir_gen_element) {
        std::shared_ptr<simulator::LLVMJit> llvm_jit = ir_gen_element->m_llvm_jit;
        RegIRCopy &reg_ir_copy = ir_gen_element->m_reg_ir_copy;
        std::shared_ptr<llvm::IRBuilder<>> ir_builder = llvm_jit->ir_builder();

        auto cst = std::dynamic_pointer_cast<Immediate32>(m_op1);
        auto dst = std::dynamic_pointer_cast<SingleReg>(m_op2);
        if (cst && dst) {
            u32 scst16 = cst->data();
            std::string dst_name = get_reg_name(dst->reg());
            llvm::Value *dst_ptr = IRGenerator::get_global_value_ptr(llvm_jit, dst_name);
            ir_builder->CreateStore(ir_builder->getInt32(scst16), dst_ptr);
        }
    }
}

